#pragma once

#include <jsoncpp/json//json.h>

#include "compiler.hpp"
#include "runner.hpp"

namespace ns_compile_run
{
    using namespace ns_compiler;
    using namespace ns_runner;
    using namespace ns_utils;
    using namespace ns_log;

    class CompileAndRun
    {
    private:
        /**
         * @brief 获取status对应描述
         *
         * @param[in] status 状态码
         *
         * @param[in] file 不带后缀的文件名，当编译错误时读取错误信息
         *
         * @return 描述信息
         */
        static string get_reason(int status,const string& file)
        {
            std::string reason;
            switch (status)
            {
                case 0:
                    reason = "运行成功";
                    break;
                case -3:
                    reason = "提交的代码为空";
                    break;
                case -2:
                    reason = "服务器资源紧张";
                    break;
                case -1:
                    FileUtil::read_file(PathUtil::compile_err_file(file),&reason,true);
                    break;
                case SIGABRT:
                    reason = "超出内存限制";
                    break;
                case SIGFPE:
                    reason = "算数运行错误";
                    break;
                case SIGSEGV:
                    reason = "非法访问内存";
                    break;
                case SIGXCPU:
                    reason = "超出时间限制";
                    break;
                default:
                    reason = "信号值：" + std::to_string(status);
                    break;
            }

            return reason;
        }

        /**
         * @brief 清理编译运行过程中产生的临时文件
         *
         * @param[in] file 不带后缀的临时文件名
         *
         */
        static void remove_temp_file(const string& file)
        {
            /* 下面6个临时文件不一定都全部存在，但不影响unlink函数调用 */
            unlink(PathUtil::compile_err_file(file).c_str());
            unlink(PathUtil::src_file(file).c_str());
            unlink(PathUtil::exec_file(file).c_str());
            unlink(PathUtil::stdin_file(file).c_str());
            unlink(PathUtil::stdout_file(file).c_str());
            unlink(PathUtil::stderr_file(file).c_str());
        }
    public:
        /**
         * @brief 编译并运行提交过来的代码
         *
         * @param[in] in_json   包含代码与运行环境的json串,由下面四个部分组成
         *                      code: 代码 input: 代码命令行参数
         *                      cpu_limit: cpu时间限制，单位为秒
         *                      mem_limit: memory空间限制，单位为KB
         *
         * @param[out] out_json 包含编译运行结果的json字符串,由下面四个部分组成
         *                      status：状态码
         *                              < 0 : 编译运行阶段出现问题
         *                              = 0 : 编译运行成功
         *                              > 0 : 编译成功，运行收到信号崩溃
         *                      reason: status对应描述信息
         *                      选填：下面两个部分为选填，只有运行成功时才会填入
         *                      stdout: 运行时标准输出
         *                      stderr: 运行时标准错误输出
         *
         */
        static void start(const string& in_json,string* out_json)
        {
            /* 反序列化json,解析出code、input、cpu_limit、mem_limit */
            Json::Value in_root;
            Json::Reader reader;
            reader.parse(in_json,in_root);
            string code = in_root["code"].asString();
            string input = in_root["input"].asString();
            int cpu_limit = in_root["cpu_limit"].asInt();
            int mem_limit = in_root["mem_limit"].asInt();

            int out_status = 0; // 状态码,假设编译运行成功
            int run_status = 0; // 程序运行状态码
            string unique_file; // 不带后缀的唯一文件名

            if(0 == code.size()) // 代码为空
            {
                out_status = -3;

                goto END;
            }

            unique_file = FileUtil::unique_file();
            if(!FileUtil::write_file(PathUtil::src_file(unique_file),code))
            {
                out_status = -2;

                goto END;
            }

            if(!Compiler::compile(unique_file)) // 编译错误
            {
                out_status = -1;

                goto END;
            }

            run_status = Runner::run(unique_file,cpu_limit,mem_limit);
            if(run_status < 0)
            {
                out_status = -2;
            }
            else if(run_status > 0)
            {
                out_status = run_status; // 运行时收到信号
            }

            END:
                Json::Value out_root;
                out_root["status"] = out_status;
                out_root["reason"] = get_reason(out_status,unique_file);

                /* 编译运行成功，填入运行过程中标准输出、标准错误输出信息 */
                if(0 == out_status)
                {
                    std::string _stdout;
                    std::string _stderr;
                    FileUtil::read_file(PathUtil::stdout_file(unique_file),&_stdout, true);
                    FileUtil::read_file(PathUtil::stderr_file(unique_file),&_stderr, true);

                    out_root["stdout"] = _stdout;
                    out_root["stderr"] = _stderr;
                }
                /* 序列化编译运行结果json串 */
                Json::StyledWriter writer;
                *out_json = writer.write(out_root);

                remove_temp_file(unique_file); // 清理编译运行产生的临时文件
        }
    };
}
